import React from 'react';
import ReactDOM, { findDOMNode } from 'react-dom';
import PropTypes from 'prop-types';

const KEYCODES = {
  ESCAPE: 27
};

export default class Portal extends React.Component {

  static propTypes = {
    children: PropTypes.element.isRequired,
    onClose: PropTypes.func,
    onOpen: PropTypes.func,
    isOpen: PropTypes.bool,
    destroyOnClose: PropTypes.bool,
    closeOnEsc: PropTypes.bool,
    closeOnOutsideClick: PropTypes.bool,
    portalTrigger: PropTypes.object, // 打开portal的触发按钮ref，用于判断 closeOnOutsideClick 点击
  };

  static defaultProps = {
    onClose: () => {},
    onOpen: () => {},
    isOpen: false,
    destroyOnClose: true,
    closeOnOutsideClick: false,
    closeOnEsc: false,
  };

  constructor(props) {
    super(props);
    this.node = null;
    this.state = {
      active: props.isOpen,
    };
    this.openPortal = this.openPortal.bind(this);
    this.closePortal = this.closePortal.bind(this);
    this.handleOutsideMouseClick = this.handleOutsideMouseClick.bind(this);
    this.handleKeydown = this.handleKeydown.bind(this);
  }

  componentDidMount() {
    if (this.props.closeOnEsc) {
      document.addEventListener('keydown', this.handleKeydown);
    }
    if (this.props.closeOnOutsideClick) {
      document.addEventListener('click', this.handleOutsideMouseClick);
    }
  }

  componentWillUpdate(nextProps) {
    if (nextProps.isOpen !== this.state.active ) {
      this.setState({
        active: nextProps.isOpen
      });
    }
  }

  componentWillUnmount() {
    if (this.props.closeOnEsc) {
      document.removeEventListener('keydown', this.handleKeydown);
    }
    if (this.props.closeOnOutsideClick) {
      document.removeEventListener('click', this.handleOutsideMouseClick);
    }
    this.removeNode();
  }

  openPortal(e) {
    if (this.state.active) {
      return;
    }
    if (e && e.nativeEvent) {
      e.nativeEvent.stopImmediatePropagation();
    }
    this.setState({ active: true }, this.props.onOpen);
  }

  closePortal() {
    if (!this.state.active) {
      return;
    }
    this.setState({ active: false }, this.props.onClose);
    if (this.props.destroyOnClose) {
      this.removeNode();
    }
  }

  createNode() { //
    if (!this.node) {
      this.node = document.createElement('div');
      document.body.appendChild(this.node);
    }
    return ReactDOM.createPortal(
      this.props.children,
      this.node,
    );
  }

  removeNode() { //
    if (this.node) {
      document.body.removeChild(this.node);
    }
    this.node = null;
    return null;
  }

  handleOutsideMouseClick(e) {
    if (!this.state.active) {
      return;
    }
    const root = this.node;
    const { portalTrigger } = this.props;
    if (!root || root.contains(e.target) || (e.button && e.button !== 0) || (portalTrigger && findDOMNode(portalTrigger).contains(e.target))) {
      return;
    }
    this.closePortal();
  }

  handleKeydown(e) {
    if (e.keyCode === KEYCODES.ESCAPE && this.state.active) {
      this.closePortal();
    }
  }

  render() {
    const { active } = this.state;
    return active ? this.createNode() : this.removeNode();
  }
}
